
function _gUpdateRect(rc, x, y)//[left, bottom, right, top]
{
    if (rc[0] > x) rc[0] = x;
	if (rc[2] < x) rc[2] = x;
	if (rc[1] > y) rc[1] = y;
	if (rc[3] < y) rc[3] = y;
}

function _gUpdateLineEndPoints(rect, start_x, start_y, end_x, end_y, hw)
{
	if (start_x == end_x) {
		if (start_y == end_y) {
			// If this is a zero-length line, we use a width box
			// TESTDOC: bug #2019 - RenderProblemExample.pdf
			//rect.UpdateRect(end_x + hw, end_y + hw); 
			//rect.UpdateRect(end_x - hw, end_y - hw);
            _gUpdateRect(rect, end_x + hw, end_y + hw);
            _gUpdateRect(rect, end_x - hw, end_y - hw);
			return;
		}
		// easy
		var point_y=0;
		if (end_y < start_y) point_y = end_y - hw;
		else point_y = end_y + hw;
	//	rect.UpdateRect(end_x + hw, point_y); 
	//	rect.UpdateRect(end_x - hw, point_y);
        _gUpdateRect(rect, end_x + hw, point_y);
        _gUpdateRect(rect, end_x - hw, point_y);
		return;
	} else if (start_y == end_y) {
		// also easy
		var point_x = 0;
		if (end_x < start_x) point_x = end_x - hw;
		else point_x = end_x + hw;
	//	rect.UpdateRect(point_x, end_y + hw);
	//	rect.UpdateRect(point_x, end_y - hw);
        _gUpdateRect(rect, point_x, end_y + hw);
        _gUpdateRect(rect, point_x, end_y - hw);
		return;
	}

	// Now we need to do some calculation. 
	// First get the middle point, which is on the extended line
	var dx = end_x - start_x;
	var dy = end_y - start_y;
	var ll = Math.sqrt(dx*dx+dy*dy);//fixsqrt2_8_8_to_8(dx, dy);
	var mx = end_x + hw * dx / ll;
	var my = end_y + hw * dy / ll;
	// now from the middle point we calculate two end points
	var dx1 = hw * dy / ll;
	var dy1 = hw * dx / ll;
//	rect.UpdateRect(mx - dx1, my + dy1);
//	rect.UpdateRect(mx + dx1, my - dy1);
    _gUpdateRect(rect, mx - dx1, my + dy1);
    _gUpdateRect(rect, mx + dx1, my - dy1);
}

function _gUpdateLineJoinPoints(rect, start_x, start_y, middle_x, middle_y, end_x, end_y, half_width, miter_limit)
{
	// Definition of some terms we used here:
	// start-point: the point sepecified by {start_x, start_y} coordinations
	// middle-point: the point sepecified by {middle_x, middle_y} coordinations
	// end-point: the point sepecified by {end_x, end_y} coordinations
	// start-line: the straight line going through start-point and middle-point
	// start-line direction: from start-point to middle-point
	// end-line: the straight line going through middle-point and end-point
	// end-line direction: from middle-point to end-point
	// side-line: a line parellel to start/end line, and its distance to start/end line is half width.
	//				Each line (start or end) has two side lines, at different sides of the line.

	var start_k = 0, start_c = 0, end_k = 0, end_c = 0, start_len = 0, start_dc = 0, end_len = 0, end_dc = 0;

	// First we need to process some special cases
	var bStartVert = Math.abs(start_x - middle_x) < 1.0/20;
	var bEndVert = Math.abs(middle_x - end_x) < 1.0/20;
	if (bStartVert && bEndVert) {
		// The end-line is also a vertical line
		// In this case we need to check the direction of two lines
	var start_dir = middle_y > start_y ? 1 : -1;
// 		int end_dir = end_y > middle_y ? 1 : -1;
// 		if (start_dir == end_dir) 
// 			// The two lines are of same direction
// 			return;

		// The two lines go different direction, then the join point is possibly at boundary
		var point_y = middle_y + half_width * start_dir;
	//	rect.UpdateRect(middle_x + half_width, point_y); 
	//	rect.UpdateRect(middle_x - half_width, point_y);
        _gUpdateRect(rect, middle_x + half_width, point_y);
        _gUpdateRect(rect, middle_x - half_width, point_y);
		return;
	}

	if (!bStartVert) {
		// The start-line is not vertical line, so we can use y = kx + c to represent it
		start_k = (middle_y - start_y)/(middle_x - start_x);
		start_c = middle_y - (start_k * middle_x);

		// calculate the projected distance between side-line and start-line at Y axis
		start_len = Math.sqrt(((start_x - middle_x)*(start_x - middle_x))+((start_y - middle_y)*(start_y - middle_y)));
		start_dc = Math.abs((half_width*start_len) /(start_x - middle_x));
	}

	if (!bEndVert) {
		// The end-line is not vertical line, so we can use y = kx + c to represent it
		end_k = (end_y - middle_y) / (end_x - middle_x);
		end_c = middle_y - (end_k * middle_x);

		// calculate the projected distance between side-line and end-line at Y axis
		end_len = Math.sqrt((end_x - middle_x)*(end_x - middle_x) + (end_y - middle_y)*(end_y - middle_y));
		end_dc = Math.abs((half_width * end_len) / (end_x - middle_x));
	}

	if (bStartVert) {
		// The start-line is vertical, while the end-line is not
		// Pick the outside X
		var outside_x = start_x;
		if (end_x < start_x) outside_x += half_width;
		else outside_x -= half_width;

		// get outside side-line of the end-line, according to which side the start-point is at
		var outside_y;
		if (start_y < (end_k * start_x) + end_c)
			outside_y = (end_k * outside_x) + end_c + end_dc;
		else
			outside_y = (end_k * outside_x) + end_c - end_dc;

		//rect.UpdateRect(outside_x, outside_y);
        _gUpdateRect(rect,outside_x, outside_y);
		return;
	}

	if (bEndVert) {
		// The end-line is a vertical line, while the start-line is not
		// Pick the outside X
		var outside_x = end_x;
		if (start_x < end_x) outside_x += half_width;
		else outside_x -= half_width;

		// get outside side-line of the start-line, according to which side the end-point is at
		var outside_y;
		if (end_y < (start_k * end_x) + start_c)
			outside_y = (start_k * outside_x) + start_c + start_dc;
		else
			outside_y = (start_k * outside_x) + start_c - start_dc;

		//rect.UpdateRect(outside_x, outside_y);
        _gUpdateRect(rect,outside_x, outside_y);
		return;
	}

	if (Math.abs(start_k - end_k) < 1.0/20) {
		// This two lines form one line. We need to check their direction
		var start_dir = middle_x > start_x ? 1 : -1;
		var end_dir = end_x > middle_x ? 1 : -1;
		if (start_dir == end_dir) 
			// The two lines are of same direction
			// We have to update the end points too. TESTDOC: bug#900
			_gUpdateLineEndPoints(rect, middle_x, middle_y, end_x, end_y, half_width);
		else
			// The two lines go different direction, then the join point is possibly at boundary
			_gUpdateLineEndPoints(rect, start_x, start_y, middle_x, middle_y, half_width);
		return;
	}

	// Now, we have a normal join. 
	// We choose the the "outside" side-line of the start/end line to calculate the join point.

	// First determine the outside side-line of start-line. To do this, we first determine
	// which side the end-point is at, then we choose the side-line in the opposite side.
	var start_outside_c = start_c;
	if (end_y < (start_k * end_x) + start_c)
		// the end-point is at lower side of start-line, so we choose the upper side-line
		start_outside_c += start_dc;
	else
		start_outside_c -= start_dc;
	// Same procedure for end-line
	var end_outside_c = end_c;
	if (start_y < (end_k * start_x) + end_c)
		// the start-point is at lower side of end-line, so we choose the upper side-line
		end_outside_c += end_dc;
	else
		end_outside_c -= end_dc;

	// Finally we can get the join point now
	var join_x = (end_outside_c - start_outside_c) / (start_k - end_k);
	var join_y = (start_k *join_x) + start_outside_c;

	//rect.UpdateRect(join_x, join_y);
	_gUpdateRect(rect, join_x, join_y);
}


function PathObj(pdObjs) {
	this.nID = null;
	this.position = []; //[l,t,r,b]
	this.pathPointsX = []; //
	this.pathPointsY = []; 
	this.pathPointsFlg = [];
	this.fillType = null;
	
    this.nGenStateID = -1;
    this.nClrStateID = -1;
    this.nGraphStateID = -1;
    this.nTextStateID = -1;
    this.nClipPathStateID = -1;
    this.fillShadingPattern = null;
    this.strokeShadingPattern = null;
    this.fillTillingPattern = null;
    this.strokeTillingPattern = null;
	this.isStrokePath = null;
	this.matrix = null;


	this.m_lsClosePathFlag = [];
	this.m_pdObjs = pdObjs;

	this.GetBoundingBox = function () {
	    var nPoint = this.pathPointsX.length;
	    if (nPoint <= 0) return;

	    var left = 0; var right = 0;
	    var top = 0; var bottom = 0;
	    if (nPoint) {
	        left = right = this.pathPointsX[0];
	        top = bottom = this.pathPointsY[0];
	        for (var i = 1; i < nPoint; i++) {
	            var x = this.pathPointsX[i];
	            var y = this.pathPointsY[i];
	            if (left > x) left = x;
	            if (right < x) right = x;
	            if (bottom > y) bottom = y;
	            if (top < y) top = y;
	        }
	    }

	    return [left, bottom, right, top];

	};

	this.GetBoundingBox2 = function (line_width, miter_limit) {
	    var rect = [100000 * 1.0, 100000 * 1.0, -100000 * 1.0, -100000 * 1.0];
	    var iPoint = 0; var half_width = line_width;
	    var iStartPoint, iEndPoint, iMiddlePoint; var bJoin;
	    var nPointCount = this.pathPointsY.length;
	    while (iPoint < nPointCount) {
	        if (this.pathPointsFlg[iPoint] == 6) {
	            // This is a start point
	            iStartPoint = iPoint + 1;
	            iEndPoint = iPoint;
	            bJoin = false;
	        } else {
	            if (this.pathPointsFlg[iPoint] == 4) {
	                // Bezier curve. Nice thing about bezier curve is, the initial direction of a curve is
	                // from the start-point to first control point, and the final direction of a curve is
	                // from the second control point to the end-point.

	                // First, check control points for possible bounding box
	                //	rect.UpdateRect(m_pPoints[iPoint].m_PointX, m_pPoints[iPoint].m_PointY);
	                var x = this.pathPointsX[iPoint];
	                var y = this.pathPointsY[iPoint];
	                _gUpdateRect(rect, x, y);
	                //	rect.UpdateRect(m_pPoints[iPoint+1].m_PointX, m_pPoints[iPoint+1].m_PointY);
	                var x1 = this.pathPointsX[iPoint + 1];
	                var y1 = this.pathPointsY[iPoint + 1];
	                _gUpdateRect(rect, x1, y1);
	                // Now get to the end point for join point or end point
	                iPoint += 2;
	            }

	            // This is a line end. But it can be join too, need to check next point
	            if (iPoint == nPointCount - 1 || this.pathPointsFlg[iPoint + 1] == 6) {
	                // This is an end point
	                iStartPoint = iPoint - 1;
	                iEndPoint = iPoint;
	                bJoin = false;
	            } else {
	                // This is a join point
	                iStartPoint = iPoint - 1;
	                iMiddlePoint = iPoint;
	                iEndPoint = iPoint + 1;
	                bJoin = true;
	            }
	        }
	        var start_x = this.pathPointsX[iStartPoint]; //m_pPoints[iStartPoint].m_PointX;
	        var start_y = this.pathPointsY[iStartPoint]; //m_pPoints[iStartPoint].m_PointY;
	        var end_x = this.pathPointsX[iEndPoint]; //m_pPoints[iEndPoint].m_PointX;
	        var end_y = this.pathPointsY[iEndPoint]; //m_pPoints[iEndPoint].m_PointY;
	        if (bJoin) {
	            var middle_x = this.pathPointsX[iMiddlePoint]; // m_pPoints[iMiddlePoint].m_PointX;
	            var middle_y = this.pathPointsY[iMiddlePoint]; // m_pPoints[iMiddlePoint].m_PointY;
	            _gUpdateLineJoinPoints(rect, start_x, start_y, middle_x, middle_y, end_x, end_y, half_width, miter_limit);
	        } else {
	            _gUpdateLineEndPoints(rect, start_x, start_y, end_x, end_y, half_width);
	        }
	        iPoint++;
	    }
	    rc = [];
	    rc[0] = rect[0];
	    rc[1] = rect[3];
	    rc[2] = rect[2];
	    rc[3] = rect[1];
	    return rc;
	}

	this.CalcBBox = function () {
	    if (this.position != null && this.position.length == 4)
	        return this.position;
	    var nPoint = this.pathPointsX.length;
	    if (nPoint == 0) return;
	    var rect = [0, 0, 0, 0];

	    var graphState = this.m_pdObjs.GetGraphStateByID(this.nGraphStateID);
	    var width = graphState.fLineWidth;

	    if (this.isStrokePath && width != 0) {
	        rect = this.GetBoundingBox2(width, graphState.fJoinMiterLimit);
	    } else {
	        rect = this.GetBoundingBox();
	    }

	    var rc = this.matrix.TransFormRect(rect[0], rect[1], rect[2], rect[3]);
	    if (width == 0 && this.isStrokePath) {
	        // Cosmetic (one pixel) line. We approximate to one point line here
	        rc[0] += -0.5;
	        rc[2] += 0.5;
	        rc[3] += -0.5;
	        rc[1] += 0.5;
	    }
	    this.position = rc;
	};

	this.GetBBox = function (mtObj2Dev) {
	    this.CalcBBox();
	    var rect = mtObj2Dev.TransFormRect(this.position[0], this.position[1], this.position[2], this.position[3]);
	    return rect;
	};
}	

// set up prototype inheritance chain
 setupInheritPrototype(PathObj, PageObj);


PathObj.prototype.SetStroke = function(bStroke){
    this.isStrokePath = bStroke;
};

PathObj.prototype.GetStroke = function(){
    return this.isStrokePath;
};


PathObj.prototype.SetID = function(pathId){
	    this.nID = pathId;
	};
PathObj.prototype.GetID = function(){
	    return this.nID;
	};
PathObj.prototype.SetPosition = function(position){
		this.position = position;
	};
PathObj.prototype.GetPosition = function(){
		return this.position;
	};
   
PathObj.prototype.SetPathPoints = function (x, y,flag) {
	    this.pathPointsX.push(x);
	    this.pathPointsY.push(y);
	    this.pathPointsFlg.push(flag);
	};
PathObj.prototype.CountPathPoints = function () {
        var nX = this.pathPointsX.length;
        var nY = this.pathPointsY.length;
        return nX > nY ? nY : nX;
    };
PathObj.prototype.GetPathPoint = function (i) {
        var n = this.CountPathPoints();
        if (i < 0 || i >= n) return null;
        var p = [];
        p[0] = this.pathPointsX[i];
        p[1] = this.pathPointsY[i];
		p[2] = this.pathPointsFlg[i];
        return p;
    };
PathObj.prototype.SetFillType = function(fillType){
		this.fillType = fillType;
	};
PathObj.prototype.GetFillType = function(){
		return this.fillType;
	};
	
PathObj.prototype.SetStrokePathFlag = function(strokePath){
	    this.isStrokePath = strokePath;
	};
PathObj.prototype.IsStrokePath = function (){
	    return this.isStrokePath;
	};


PathObj.prototype.ParseJson = function (arrayData) {
    //var path = this;
    this.SetID(arrayData[1]);
    this.SetFillType(arrayData[11]);
    this.SetPosition(stringArr2NumArr(arrayData[2]));
    this.SetStrokePathFlag(arrayData[10]);
    this.SetMatrix(stringArr2NumArr(arrayData[3]));
    this.nGraphStateID = arrayData[6];
    this.nClrStateID = arrayData[7]; 
    this.nTextStateID = arrayData[8];
    this.nGenStateID = arrayData[4];
    this.nClipPathStateID = arrayData[5]; 
    
    for (var i = 0; i < arrayData[9].length; i += 1) {
        this.SetPathPoints(parseFloat(arrayData[9][i][1]), parseFloat(arrayData[9][i][2]), arrayData[9][i][0]);
    }
};

PathObj.prototype.ParseFromBin = function (rd) {
    //    var type = rd.readByte(); //
    var nID = rd.readInt32();
    this.SetID(nID);

    var nMatrixID = rd.readInt16();
    this.matrix = this.m_pdObjs.GetMatrixByID(nMatrixID);

    var bStroke = rd.readByte();
    this.SetStroke(bStroke);

    var fillType = rd.readByte();
    this.SetFillType(fillType);
    
    var nPtCount = rd.readInt32();
    for (var i = 0; i < nPtCount; i++) {
    	this.pathPointsFlg.push(rd.readByte());
	    this.pathPointsX.push(rd.readFloat32());
		this.pathPointsY.push(rd.readFloat32());
    }

/*
    var nPath = rd.readInt32();

    for (var i = 0; i < nPath; i++) {
        var nFlag = rd.readByte();

        //    this.pathPointsFlg.push(nFlagtmp);

        var bClosPath = rd.readByte();
        this.m_lsClosePathFlag.push(bClosPath);
        var nPoint = rd.readInt32();

        switch (nFlag) {
            case 0: //line
                {
                    for (var j = 0; j < nPoint; j++) {
                        var n = rd.readFloat32();
                        if (j % 2)
                            this.pathPointsY.push(n);
                        else
                            this.pathPointsX.push(n);


                    }
                    var nP = nPoint / 2;
                    this.pathPointsFlg.push(6);
                    for (var k = 1; k < nP; k++) {
                        this.pathPointsFlg.push(2);
                    }

                }
                break;
            case 1: //rect
                {
                    var x = rd.readFloat32();
                    var y = rd.readFloat32();
                    var w = rd.readFloat32();
                    var h = rd.readFloat32();

                    this.pathPointsFlg.push(6); //moveTo
                    this.pathPointsX.push(x);
                    this.pathPointsY.push(y);

                    this.pathPointsFlg.push(2); //lineTo
                    this.pathPointsX.push(x + w);
                    this.pathPointsY.push(y);

                    this.pathPointsFlg.push(2); //lineTo
                    this.pathPointsX.push(x + w);
                    this.pathPointsY.push(y + h);

                    this.pathPointsFlg.push(2); //lineTo
                    this.pathPointsX.push(x);
                    this.pathPointsY.push(y + h);

                    this.pathPointsFlg.push(2); //lineTo
                    this.pathPointsX.push(x);
                    this.pathPointsY.push(y);
                }

                break;
            case 2: //B��zier
                {
                    for (var j = 0; j < nPoint; j++) {
                        var n = rd.readFloat32();
                        if (j % 2)
                            this.pathPointsY.push(n);
                        else
                            this.pathPointsX.push(n);

                  

                    }
                    var nP = nPoint / 2;
                    this.pathPointsFlg.push(6);
                    for (var k = 1; k < nP; k++) {
                        this.pathPointsFlg.push(4);
                    }
                }
                break;
        }
    }
*/
};